package com.base.util;

import java.io.*;
import java.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * Created by Sihsan on 2017/7/24.
 */
public class Security {

    private static String cStr[] = new String[]{"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","=",".","?","/","*","&","%","#","@","!","<",">","|","1","2","3","4","5","6","7","8","9","0","我","真","的","是","太","帅","了"};

    private static LinkedList<String> cLink = new LinkedList<>();

    private static Map<String,List<String>> numMap = new HashMap<>();

    private static Map<String,String> characterMap = new HashMap<>();

    private static void initSecGroup(){
        for(int i=0;i<=9;i++){
            numMap.put(String.valueOf(i),new ArrayList<>());
        }
        numMap.put("-",new ArrayList<>());
    }

    private static void init(){
        initSecGroup();
        if(numMap.isEmpty()){
            throw new RuntimeException("没有需要打乱的！");
        }
        if(numMap.size() > cStr.length){
            throw new RuntimeException("无法初始化，乱象编码太少了！");
        }
        for(String s:cStr){
            cLink.add(s);
        }
        Set<String> keySet = numMap.keySet();
        List<String> keyList = new ArrayList<>(keySet);

        //初始化第一次，保证每一个都拥有数据
        int index;
        String c;
        for(String k:numMap.keySet()){
            index = new Random().nextInt(cLink.size());
            numMap.get(k).add(c=cLink.get(index));
            characterMap.put(c,k);
            cLink.remove(index);
        }
        if(cLink.size()>0){
            String k;
            while(true){
                index = new Random().nextInt(cLink.size());
                numMap.get(k=keyList.get(new Random().nextInt(keyList.size()))).add(c=cLink.get(index));
                characterMap.put(c,k);
                cLink.remove(index);
                if(cLink.isEmpty()){
                    break;
                }
            }
        }

    }

    static{
        try {
            readKeyContext("key.dat");
        } catch (Exception e) {
            initSecGroup();
            init();
            try {
                saveKeyContext("key.dat");
            } catch (Exception e1) {
            }
        }
    }

    public static String addKey(byte[] data,int end,int c) throws Exception{
        if(end<=0){
            end = data.length;
        }
        int d;
        String buff="";
        for(int i=0;i<end;i++){
            int b = data[i];
            d = ~b+c;
            if(!buff.isEmpty()){
                buff+=",";
            }
            buff+=getMap(String.valueOf(d));
        }
        return gzipStr(Base64.getEncoder().encodeToString(buff.getBytes("UTF-8")));
    }

    public static void addKey(InputStream is,OutputStream os,int c){
        byte b[] = new byte[5120];
        int read;
        byte sc[];
        try {
            while((read=is.read(b)) >0){
                os.write((addKey(b,read,103)+"\n\n\n\n").getBytes("ISO-8859-1"));
            }
            os.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try{
                os.close();
            }catch (Exception e){}
            try{
                is.close();
            }catch (Exception e){}
        }
    }

    public static void removeKey(InputStream is,OutputStream os,int c){
        try {

            byte barr[] = new byte[1024];
            int b1 = is.read();
            if (b1 == -1) {
                return;
            }
            barr[0] = (byte)b1;
            int times = 0;
            int i = 1;
            while(true){
                try{
                    b1 = is.read();
                    if(b1 == -1){
                        break;
                    }
                }catch(Exception e){
                    times=0;
                    break;
                }
                barr[i++] = (byte)b1;
                if(i>=barr.length){
                    byte []barr2 = new byte[barr.length*2];
                    for(int index=0;index<barr.length;index++){
                        barr2[index]=barr[index];
                    }
                    barr=barr2;
                }
                if(b1 == 10){
                    times++;
                    if(times >= 4){
                        os.write(removeKey(new String(barr,0,i-4,"ISO-8859-1"),c));
                        times = 0;
                        i=0;
                    }
                }else{
                    times=0;
                }
            }
            if(i>0){
                os.write(removeKey(new String(barr,0,i,"ISO-8859-1"),c));
                os.flush();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try{
                is.close();
            }catch (Exception e){}
            try{
                os.close();
            }catch (Exception e){}
        }

    }

    public static byte[] removeKey(String data,int c) throws Exception{
        data = new String(Base64.getDecoder().decode(unzipStr(data)),"UTF-8");
        String dataArr[] = data.split(",");
        if(dataArr == null|| dataArr.length == 0){
            return null;
        }
        if(dataArr == null || dataArr.length == 0){
            return null;
        }
        byte []b = new byte[dataArr.length];
        int index=0;
        Integer k;
        for(String str:dataArr){
            k = Integer.parseInt(getOriginal(str));
            k = k-c;
            b[index] = (byte)~k.intValue();
            index++;
        }
        return b;
    }

    private static String getMap(String key){
        String k="";
        String t;
        for(int i=0;i<key.length();i++){
            t = key.substring(i,i+1);
            k+=numMap.get(t).get(new Random().nextInt(numMap.get(t).size()));
        }
        return k;
    }

    private static String getOriginal(String key){
        String k="";
        String t;
        for(int i=0;i<key.length();i++){
            t = key.substring(i,i+1);
            k+=characterMap.get(t);
        }
        return k;
    }

    private static String gzipStr(String str) throws Exception{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        GZIPOutputStream gout = new GZIPOutputStream(bos);
        gout.write(str.getBytes("UTF-8"));
        gout.flush();
        gout.finish();
        String se = bos.toString("ISO-8859-1");
        bos.close();
        gout.close();
        return se;
    }
    private static String unzipStr(String str) throws Exception{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(str
                .getBytes("ISO-8859-1"));
        GZIPInputStream gizip = new GZIPInputStream(in);
        byte bb[] = new byte[1024];
        int read;
        while((read=gizip.read(bb))>0){
            bos.write(bb,0,read);
        }
        String d =  bos.toString("UTF-8");
        bos.close();
        gizip.close();
        in.close();
        return d;
    }

    private static void readKeyContext(String path) throws Exception{
        FileInputStream fis = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fis);
        List<Object> list = (List<Object>) ois.readObject();
        numMap = (Map<String, List<String>>) list.get(0);
        characterMap = (Map<String, String>) list.get(1);
        ois.close();
        fis.close();
    }

    private static void saveKeyContext(String path) throws Exception{
        FileOutputStream fos = new FileOutputStream(path);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        List<Object> list = new ArrayList<>();
        list.add(numMap);
        list.add(characterMap);
        oos.writeObject(list);
        oos.flush();
        fos.flush();
        oos.close();
        fos.close();
    }

    public static void main(String args[]) throws  Exception{
        /*try{
            readKeyContext("E:/key.dat");
        }catch (Exception e){
            e.printStackTrace();
            init();
            saveKeyContext("E:/key.dat");
        }
        System.out.println("比对库："+numMap);
        System.out.println("恢复映射："+characterMap);

        String str="登录时            ,我们                                   填写的是明文密码,                 但在数据                      库里存储的是加密后的密码\n测试了一遍又一遍";
        System.out.println("加密前："+str);
        byte strByte[] = str.getBytes();
        String sec;
        System.out.println("加密后的：\n"+(sec=addKey(strByte,0,100)));
        System.out.println("解密后的："+new String(removeKey(sec,100)));*/

        long startTime = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("E:/tools/Microsoft Toolkit.exe");
        FileOutputStream fos = new FileOutputStream("E:/tools/Microsoft Toolkit.sec");
        addKey(fis,fos,103);
        long endTime = System.currentTimeMillis();

        System.out.println("用时："+(endTime-startTime));

    }

}
